{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/timsainb/tensorflow2-generative-models/blob/master/0.0-Autoencoder-fashion-mnist.ipynb)\n",
    "## Autoencoder (AE) \n",
    "\n",
    "A simple autoencoder network.\n",
    "\n",
    "![an autoencoder](imgs/ae.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Install packages if in colab"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "### install necessary packages if in colab\n",
    "def run_subprocess_command(cmd):\n",
    "    process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)\n",
    "    for line in process.stdout:\n",
    "        print(line.decode().strip())\n",
    "\n",
    "\n",
    "import sys, subprocess\n",
    "\n",
    "IN_COLAB = \"google.colab\" in sys.modules\n",
    "colab_requirements = [\"pip install tf-nightly-gpu-2.0-preview==2.0.0.dev20190513\"]\n",
    "if IN_COLAB:\n",
    "    for i in colab_requirements:\n",
    "        run_subprocess_command(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### load packages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-05-13T20:25:37.755829Z",
     "start_time": "2019-05-13T20:25:33.487278Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/mnt/cube/tsainbur/conda_envs/tpy3/lib/python3.6/site-packages/tqdm/autonotebook/__init__.py:14: TqdmExperimentalWarning: Using `tqdm.autonotebook.tqdm` in notebook mode. Use `tqdm.tqdm` instead to force console mode (e.g. in jupyter console)\n",
      "  \" (e.g. in jupyter console)\", TqdmExperimentalWarning)\n"
     ]
    }
   ],
   "source": [
    "import tensorflow as tf\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from tqdm.autonotebook import tqdm\n",
    "%matplotlib inline\n",
    "from IPython import display\n",
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-05-13T20:25:37.764933Z",
     "start_time": "2019-05-13T20:25:37.759571Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.0.0-alpha0\n"
     ]
    }
   ],
   "source": [
    "print(tf.__version__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create a fashion-MNIST dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-05-13T20:25:37.849926Z",
     "start_time": "2019-05-13T20:25:37.767846Z"
    }
   },
   "outputs": [],
   "source": [
    "TRAIN_BUF=60000\n",
    "BATCH_SIZE=512\n",
    "TEST_BUF=10000\n",
    "DIMS = (28,28,1)\n",
    "N_TRAIN_BATCHES =int(TRAIN_BUF/BATCH_SIZE)\n",
    "N_TEST_BATCHES = int(TEST_BUF/BATCH_SIZE)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-05-13T20:25:43.715872Z",
     "start_time": "2019-05-13T20:25:37.852434Z"
    }
   },
   "outputs": [],
   "source": [
    "# load dataset\n",
    "(train_images, _), (test_images, _) = tf.keras.datasets.fashion_mnist.load_data()\n",
    "\n",
    "# split dataset\n",
    "train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype(\n",
    "    \"float32\"\n",
    ") / 255.0\n",
    "test_images = test_images.reshape(test_images.shape[0], 28, 28, 1).astype(\"float32\") / 255.0\n",
    "\n",
    "# batch datasets\n",
    "train_dataset = (\n",
    "    tf.data.Dataset.from_tensor_slices(train_images)\n",
    "    .shuffle(TRAIN_BUF)\n",
    "    .batch(BATCH_SIZE)\n",
    ")\n",
    "test_dataset = (\n",
    "    tf.data.Dataset.from_tensor_slices(test_images)\n",
    "    .shuffle(TEST_BUF)\n",
    "    .batch(BATCH_SIZE)\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define the network as tf.keras.model object"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-05-13T20:25:43.733494Z",
     "start_time": "2019-05-13T20:25:43.718546Z"
    }
   },
   "outputs": [],
   "source": [
    "class AE(tf.keras.Model):\n",
    "    \"\"\"a basic autoencoder class for tensorflow\n",
    "    Extends:\n",
    "        tf.keras.Model\n",
    "    \"\"\"\n",
    "    def __init__(self, **kwargs):\n",
    "        super(AE, self).__init__()\n",
    "        self.__dict__.update(kwargs)\n",
    "        \n",
    "        self.enc = tf.keras.Sequential(self.enc)\n",
    "        self.dec = tf.keras.Sequential(self.dec)\n",
    "\n",
    "    @tf.function\n",
    "    def encode(self, x):\n",
    "        return self.enc(x)\n",
    "\n",
    "    @tf.function\n",
    "    def decode(self, z):\n",
    "        return self.dec(z)\n",
    "    \n",
    "    @tf.function\n",
    "    def compute_loss(self, x):\n",
    "        z = self.encode(x)\n",
    "        _x = self.decode(z)\n",
    "        ae_loss = tf.reduce_mean(tf.square(x - _x))\n",
    "        return ae_loss\n",
    "    \n",
    "    @tf.function\n",
    "    def compute_gradients(self, x):\n",
    "        with tf.GradientTape() as tape:\n",
    "            loss = self.compute_loss(x)\n",
    "        return tape.gradient(loss, self.trainable_variables)\n",
    "\n",
    "    def train(self, train_x):    \n",
    "        gradients = self.compute_gradients(train_x)\n",
    "        self.optimizer.apply_gradients(zip(gradients, self.trainable_variables)) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define the network architecture"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-05-13T20:25:43.866475Z",
     "start_time": "2019-05-13T20:25:43.735636Z"
    }
   },
   "outputs": [],
   "source": [
    "N_Z = 64\n",
    "encoder = [\n",
    "    tf.keras.layers.InputLayer(input_shape=DIMS),\n",
    "    tf.keras.layers.Conv2D(\n",
    "        filters=32, kernel_size=3, strides=(2, 2), activation=\"relu\"\n",
    "    ),\n",
    "    tf.keras.layers.Conv2D(\n",
    "        filters=64, kernel_size=3, strides=(2, 2), activation=\"relu\"\n",
    "    ),\n",
    "    tf.keras.layers.Flatten(),\n",
    "    tf.keras.layers.Dense(units=N_Z),\n",
    "]\n",
    "\n",
    "decoder = [\n",
    "    tf.keras.layers.Dense(units=7 * 7 * 64, activation=\"relu\"),\n",
    "    tf.keras.layers.Reshape(target_shape=(7, 7, 64)),\n",
    "    tf.keras.layers.Conv2DTranspose(\n",
    "        filters=64, kernel_size=3, strides=(2, 2), padding=\"SAME\", activation=\"relu\"\n",
    "    ),\n",
    "    tf.keras.layers.Conv2DTranspose(\n",
    "        filters=32, kernel_size=3, strides=(2, 2), padding=\"SAME\", activation=\"relu\"\n",
    "    ),\n",
    "    tf.keras.layers.Conv2DTranspose(\n",
    "        filters=1, kernel_size=3, strides=(1, 1), padding=\"SAME\", activation=\"sigmoid\"\n",
    "    ),\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-05-10T18:40:40.306731Z",
     "start_time": "2019-05-10T18:40:40.292930Z"
    }
   },
   "source": [
    "### Create Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-05-13T20:25:44.824895Z",
     "start_time": "2019-05-13T20:25:43.868785Z"
    }
   },
   "outputs": [],
   "source": [
    "# the optimizer for the model\n",
    "optimizer = tf.keras.optimizers.Adam(1e-3)\n",
    "# train the model\n",
    "model = AE(\n",
    "    enc = encoder,\n",
    "    dec = decoder,\n",
    "    optimizer = optimizer,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Train the model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-05-13T20:25:45.751490Z",
     "start_time": "2019-05-13T20:25:44.829179Z"
    }
   },
   "outputs": [],
   "source": [
    "# exampled data for plotting results\n",
    "example_data = next(iter(train_dataset))\n",
    "\n",
    "def plot_reconstruction(model, example_data, nex=5, zm=3):\n",
    "\n",
    "    example_data_reconstructed = model.decode(model.encode(example_data))\n",
    "    fig, axs = plt.subplots(ncols=nex, nrows=2, figsize=(zm * nex, zm * 2))\n",
    "    for exi in range(nex):\n",
    "        axs[0, exi].matshow(\n",
    "            example_data.numpy()[exi].squeeze(), cmap=plt.cm.Greys, vmin=0, vmax=1\n",
    "        )\n",
    "        axs[1, exi].matshow(\n",
    "            example_data_reconstructed.numpy()[exi].squeeze(),\n",
    "            cmap=plt.cm.Greys,\n",
    "            vmin=0,\n",
    "            vmax=1,\n",
    "        )\n",
    "    for ax in axs.flatten():\n",
    "        ax.axis(\"off\")\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-05-13T20:25:45.767634Z",
     "start_time": "2019-05-13T20:25:45.753888Z"
    }
   },
   "outputs": [],
   "source": [
    "# a pandas dataframe to save the loss information to\n",
    "losses = pd.DataFrame(columns = ['MSE'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-05-13T20:30:50.189268Z",
     "start_time": "2019-05-13T20:25:45.769876Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 49 | MSE: 0.005124256946146488\n"
     ]
    },
    {
     "data": {
      "image/png": "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7LtRMmVd5SZp82TXRsnxJJUMPPHEEyXbvn17yY4cOVKytKE0fXat5Y2qaRN410b3PrrKBPoWiqTSk127dpUsve7W8nWwYcOGkl188cUl++pXv1qym2++uWQT4TycaPrOQYcPHy7Zzp0743P+7//+b8lSMUQ6PhVApMKE0Urv8aMf/WjJUvFPa61dffXVJZvshSBprkrzcVf50aWXXtrrsamgqW/BQNcc3/e7mzFjRslSSdL69et7PV9r+f0oBJl40rn8yCOPlOwv//IvS3bgwIH4nOn3uO+95rvf/e6SpfN7JNJ7TGVj6Z6/S7q+0v1bl8k9KwMAAAyExRkAAMAAWJwBAAAMgMUZAADAACgE6SmVJjz11FMlW7x4ccmWL19esgULFvQao7XWDh48WLL58+eXrO/m4LTJsrXWjh8/XrKtW7eW7P777y9ZKrlIRQq/+Iu/GMd+1ateVbKR/GvqQ5Ve7/vf//6SfehDHypZKpdJm8+7vs9UhpCk867v59x1zqYN7KkMJhV9pMetXr06jpMKDr75zW+WLJ3bN9xwQ3xOhiVt1t68eXPJfvCDH5QsFRx0bcpO53w6j5csWVKyEydOlCwV07SW30+au9N1nX5zUqlOKpVoLV+vXWUTk0XfUo6uuS6Vez333HMlS99xGifNVV1zfN+CgVRsc+WVV5as6z2m8/vxxx8v2ctf/vJer4fzL533aX75yEc+UrKf/OQnJUvFdakoqbXWjh49WrJ07n3uc58r2X333VeyX/3VX43jvO51rytZur7S65k3b17J0tzfVXI32qIlfzkDAAAYAIszAACAAbA4AwAAGACLMwAAgAGY3DuBg7RJsrXWnn/++ZKlTZFr164tWdromP7l9LQpsStPGxPThsy02XvdunVxnHvuuadke/bsKdmiRYtKdvPNN5fskksu6XVsaxN3U3oqGbj++utLdscdd5QslbHs3LmzZO95z3vi2OkcS9L5mTagT506tWSp3KC11mbMmNErS5tpU9lDV3nH+973vpIdOXLkBY893gpnxqtUMNBaPmcfe+yxkv3whz8sWfruUinO3r1749hpA3c6v9M4hw4d6vUaW2vtmmuuKVkqkvre975Xsi996Uslmzt3bsm6yoC6rtfJLH136VzoOmfT/JnuF9LjUtZ1D5KkQpD0nOk9plKykRTJ/OhHPyrZW97ylng8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\n",
      "text/plain": [
       "<Figure size 1080x432 with 10 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "n_epochs = 50\n",
    "for epoch in range(n_epochs):\n",
    "    # train\n",
    "    for batch, train_x in tqdm(\n",
    "        zip(range(N_TRAIN_BATCHES), train_dataset), total=N_TRAIN_BATCHES\n",
    "    ):\n",
    "        model.train(train_x)\n",
    "    # test on holdout\n",
    "    loss = []\n",
    "    for batch, test_x in tqdm(\n",
    "        zip(range(N_TRAIN_BATCHES), train_dataset), total=N_TRAIN_BATCHES\n",
    "    ):\n",
    "        loss.append(model.compute_loss(train_x))\n",
    "    losses.loc[len(losses)] = np.mean(loss, axis=0)\n",
    "    # plot results\n",
    "    display.clear_output()\n",
    "    print(\"Epoch: {} | MSE: {}\".format(epoch, losses.MSE.values[-1]))\n",
    "    plot_reconstruction(model, example_data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-05-13T20:30:50.390284Z",
     "start_time": "2019-05-13T20:30:50.191575Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7f82781561d0>]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(losses.MSE.values)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "collapsed_sections": [],
   "provenance": [],
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
